/*
 Copyright 2009 Christian Sprecher

  Licensed under the Apache License, Version 2.0 (the "License");
  you may not use this file except in compliance with the License.
  You may obtain a copy of the License at

      http://www.apache.org/licenses/LICENSE-2.0

  Unless required by applicable law or agreed to in writing, software
  distributed under the License is distributed on an "AS IS" BASIS,
  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
  See the License for the specific language governing permissions and
  limitations under the License.
 */

import javax.swing.ComboBoxModel
import javax.swing.DefaultComboBoxModel
import javax.jcr.SimpleCredentials
import org.apache.jackrabbit.core.TransientRepository
import javax.swing.tree.*
import javax.swing.event.TreeSelectionListener
import javax.jcr.Node as JcrNode
import javax.jcr.Session
import javax.swing.event.TreeModelListener
import javax.swing.event.TreeModelEvent
import widgets.JcrDefaultMutableTreeNode
import helpers.JcrTreeHelper
import ca.odell.glazedlists.EventList
import ca.odell.glazedlists.BasicEventList
import ca.odell.glazedlists.SortedList
import ca.odell.glazedlists.swing.EventTableModel
import javax.swing.event.ListSelectionListener
import ca.odell.glazedlists.swing.TableComparatorChooser
import helpers.JcrPropertiesTableFormat
import ca.odell.glazedlists.gui.AbstractTableComparatorChooser
import javax.jcr.Repository
import java.rmi.registry.Registry
import org.apache.jackrabbit.rmi.client.*
import javax.jcr.PropertyType
import helpers.JcrHelper
import javax.swing.event.TreeWillExpandListener
import java.awt.Point
import java.awt.event.MouseAdapter
import widgets.PopupMenuMouseListener
import java.awt.event.ActionListener
import javax.jcr.ValueFactory
import javax.jcr.Value



class JcrBrowserController {

    // these will be injected by Griffon
    def model
    def view
    def builder
    JcrTreeHelper treeHelper
    JcrHelper helper

    def loginType = new DefaultComboBoxModel(['Remote', 'Local'] as Vector)
    def propertyTypeModel = new DefaultComboBoxModel([PropertyType.TYPENAME_BINARY, PropertyType.TYPENAME_BOOLEAN, PropertyType.TYPENAME_DATE, PropertyType.TYPENAME_DOUBLE, PropertyType.TYPENAME_LONG, PropertyType.TYPENAME_NAME, PropertyType.TYPENAME_PATH, PropertyType.TYPENAME_REFERENCE, PropertyType.TYPENAME_STRING] as Vector)

    def init() {
        doOutside {
            view.mainTabbedPane.setEnabledAt(1, false)
            view.connectionType.model = loginType
            view.propertyType.model = propertyTypeModel
            view.propertiesTable.selectionModel.addListSelectionListener({e ->
                view.valuePanel.removeAll()
                if (e.source.maxSelectionIndex >= 0) {
                    model.currentProperty.path = view.propertiesTable.model.getElementAt(e.source.maxSelectionIndex)
                    //binding doesn't work both ways atm
                    helper.withProperty(model.currentProperty.path) {property ->
                        model.currentProperty.name = property.name
                        model.currentProperty.type = PropertyType.nameFromValue(property.type)
//                        model.currentProperty.value= property.getString()
                        createMVCGroup('PropertyViewer', 'test', [property: property, parentPanel: view.valuePanel, mode: PropertyViewerModel.VIEW_MODE, helper: helper])
//                        edt { view.propertyDisplay.displayProperty(property)}
                    }
                } else {
                    model.currentProperty.clear()
                }
                edt {
                    view.valuePanel.validate()
                    view.valuePanel.repaint()
                }
            } as ListSelectionListener)
            //TODO: consolidate the whole dnd stuff. Currently no dnd within table, except for c&p
/*
            new FileDrop( view.propertyDropZone, {
                File[] files -> files.each {
                    println it.canonicalPath
                }
            } as FileDrop.Listener)
*/
            new FileDrop(view.browserTree, {
                File[] files ->
                files.each {
                    helper.addFile(model.currentNode, it.absolutePath)
                    //def contents = new JcrDefaultMutableTreeNode(rootNode.path)
                    int index = view.browserTree.lastSelectedPathComponent.childCount
                    treeHelper.addChildNode(view.browserTree.lastSelectedPathComponent,
                            view.browserTree.lastSelectedPathComponent.userObject + "/" + it.name)
                    view.browserTree.model.nodesWereInserted(view.browserTree.lastSelectedPathComponent, [index] as int[])
                }
            } as FileDrop.Listener)
            view.browserTree.addMouseListener(new PopupMenuMouseListener(view.browserTreePopup))
        }
    }

    def changeLoginType = {e ->
        if (view.connectionType.selectedItem == "Remote") {
            view.connectionURI.text = 'rmi://localhost:1099/jackrabbit'

        } else {
            view.connectionURI.text = './repository.xml'

        }
    }

    def log(logString) {
        edt {
            if (logString) {
                def caretPosition = view.info.text.size() + logString.size() + 1
                view.info.append "\n" + logString
                view.info.caretPosition = caretPosition
            }
        }
    }

    def loadBrowserModel() {
        doOutside {
            log("loading browser model")
            Session session = model.session
            JcrNode rootNode = session.rootNode
            def contents = new JcrDefaultMutableTreeNode(rootNode.path, rootNode.hasNodes())

            rootNode.nodes.each {childNode ->
                treeHelper.addChildNode(contents, childNode.path)
            }
            view.browserTree.model = new DefaultTreeModel(contents)
            view.browserTree.addTreeSelectionListener(treeSelectionListener)
            view.browserTree.addTreeWillExpandListener(treeWillExpandListener)
        }
    }

    def treeWillExpandListener = [
            treeWillExpand: {event ->
                def treeNode = event.path.lastPathComponent
                helper.withNode(treeNode.userObject) {node ->
                    //reload in any case...
                    if (treeNode.childCount > 0) {
                        treeNode.removeAllChildren()
                    }
                    if (node.hasNodes()) {
                        node.nodes.each {childNode ->
                            treeHelper.addChildNode(treeNode, childNode.path)
                        }
                    }
                }

            },
            treeWillCollapse: {event -> }
    ] as TreeWillExpandListener

    def treeSelectionListener = {event ->
        createTableModel()
        helper.withNode(model.currentNode) {node ->
            view.multiMediaPanel.removeAll()
            if (node.hasNode("jcr:content")) {
                createMVCGroup('PropertyViewer', 'test', [property: node.getNode("jcr:content").getProperty("jcr:data"), parentPanel: view.multiMediaPanel, helper: helper])
            }
            edt {
                view.multiMediaPanel.validate()
                view.multiMediaPanel.repaint()
            }
        }
    } as TreeSelectionListener

    private def createTableModel() {
        def currentNode = view.browserTree.lastSelectedPathComponent?.userObject
        model.currentNode = currentNode
        //view.nodeType?.text = helper.withNode (currentNode){ node -> node?.primaryNodeType?.name }
        EventList submodelList = new BasicEventList()
        submodelList.clear()
        helper.withNode(currentNode) {node ->
            node.properties.each {property ->
                submodelList.add(property.path)
            }
        }
        SortedList sortedProcessList = new SortedList(submodelList, {a, b -> b <=> a} as Comparator)
        view.propertiesTable.model = new EventTableModel(sortedProcessList, new JcrPropertiesTableFormat(model.session))


        def tableSorter = new TableComparatorChooser(view.propertiesTable, sortedProcessList, AbstractTableComparatorChooser.SINGLE_COLUMN)

    }

    def addProperty = {
        doOutside {
            helper.withNode(model.currentNode) {node ->
                //TODO: this is bit stupid default default value handling
                //TODO: move this to helper
                try {
                    def type = PropertyType.valueFromName(view.propertyType.selectedItem)
                    def isMultiple = view.propertyMultiple.selected
                    def name = view.newPropertyName.text
                    def path = node.path + "/" + name
                    helper.createProperty(path, type, isMultiple)
                    node.save()
                    createTableModel()
                } catch (Exception e) {
                    edt { log e.toString() }
                }
            }
        }
    }
    def removeProperty = {
        doOutside {
            try {
                helper.withProperty(model.currentProperty.path) {property ->
                    property.remove()
                }
                createTableModel()
            } catch (Exception e) {
                edt {
                    log e.toString()
                }
            }
        }
    }


    def addNode = {
        doOutside {
            def currentNode = model.currentNode
            if (currentNode) {
                try {
                    edt { log "Adding Node to ${currentNode}" }
//                    model.currentNode =treeHelper.addNode(view.browserTree.lastSelectedPathComponent,view.nodeName.text)
                    model.currentNode = treeHelper.addNode(view.browserTree.lastSelectedPathComponent, model.newNodeName, model.newNodeType)
                } catch (Exception e) {
                    edt { log e.toString() }
                }
            } else {
                edt { log "Adding Node: No selection found" }
            }
        }
    }

    def renameNode = {
        doOutside {
            def currentNode = model.currentNode
            if (currentNode) {
                try {
                    edt { log "Renaming Node ${currentNode}" }
                    treeHelper.renameNode(view.browserTree.lastSelectedPathComponent, view.nodeName.text)
                    model.session.save()
                } catch (Exception e) {
                    edt { log e.toString() }
                }
            } else {
                edt { log "Removing Node: No selection found" }
            }
        }
    }
    def removeNode = {
        doLater {

            // view.browserTree.lastSelectedPathComponent?.userObject
            def currentSelection = view.browserTree.selectionPaths*.lastPathComponent
            println currentSelection
//            def currentNode = model.currentNode
            currentSelection.each {path ->
                try {

//                    def currentTreeNode = view.browserTree.lastSelectedPathComponent
                    //def currentNodeParent = currentNode.parent
                    edt { log "Removing Node ${path}" }
                    helper.withNode(path.userObject) {node ->
                        node.remove()
                    }
                    model.session.save()
                    //model.currentNode = currentNodeParent
                    path.userObject = null
                    //     edt{
                    view.browserTree.setSelectionPath(null)
                    view.browserTree.model.removeNodeFromParent(path)
                    //     }

                } catch (Exception e) {
                    edt { log e.toString() }
                }
            }
        }
    }

    def login = {
        doOutside {
            def result = true
            try {
                edt {
                    loginButton.enabled = false
                    logoutButton.enabled = false
                    log "logging in ..."
                }

                Repository repository
                if (view.connectionType.selectedItem == 'Local') {
                    //TODO: other place
                    if (view.connectionURI == null) {
                        repository = new TransientRepository()
                    } else {
                        //assume name = directory + ".xml"
                        def configFile = view.connectionURI.text

                        repository = new TransientRepository(configFile, configFile.substring(0, configFile.lastIndexOf(".") ))
                    }
//                    repository = new TransientRepository()
                } else {
                    ClientRepositoryFactory factory = new ClientRepositoryFactory();
                    repository = factory.getRepository(view.connectionURI.text)
                }
                model.session = repository.login(
                        new SimpleCredentials(view.username.text, view.password.text.toCharArray()))
                model.user = view.username.text
                treeHelper = new JcrTreeHelper(view.browserTree, model.session)
                helper = new JcrHelper(model.session)
                view.browserTree.transferHandler.session = model.session
                view.browserTree.cellRenderer.helper = helper
                loadBrowserModel()
            } catch (Exception e) {
                edt {
                    loginButton.enabled = true
                    logoutButton.enabled = true
                    view.mainTabbedPane.setEnabledAt(1, false)
                    log e.toString()
                }
                result = false
            }
            doLater {
                if (result) {
                    loginButton.enabled = true
                    logoutButton.enabled = true
                    view.mainTabbedPane.setEnabledAt(1, true)
                    log "logged in user ${model.user}"
                }
            }
        }
    }
    def logout = {
        doOutside {
            try {
                edt {
                    log "logging out ..."
                }
                //TODO: RMI and other location
                model.session.logout()
            } catch (Exception e) {
                edt {
                    log e.toString()
                }
            }
            edt {
                log "logged out"
                view.mainTabbedPane.setEnabledAt(1, false)
            }
        }
    }
}